package com.mangosteen.interviewji.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mangosteen.interviewji.annotation.HotKeyCheck;
import com.mangosteen.interviewji.common.ErrorCode;
import com.mangosteen.interviewji.constant.CommonConstant;
import com.mangosteen.interviewji.exception.BusinessException;
import com.mangosteen.interviewji.exception.ThrowUtils;
import com.mangosteen.interviewji.manager.AiManager;
import com.mangosteen.interviewji.manager.CounterManager;
import com.mangosteen.interviewji.mapper.QuestionMapper;
import com.mangosteen.interviewji.model.dto.question.QuestionEsDTO;
import com.mangosteen.interviewji.model.dto.question.QuestionQueryRequest;
import com.mangosteen.interviewji.model.entity.*;
import com.mangosteen.interviewji.model.vo.QuestionVO;
import com.mangosteen.interviewji.model.vo.UserVO;
import com.mangosteen.interviewji.service.*;
import com.mangosteen.interviewji.utils.EsAvailable;
import com.mangosteen.interviewji.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 题目表服务实现
 *
 */
@Service
@Slf4j
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {

    @Resource
    private UserService userService;

    @Resource
    @Lazy
    private QuestionBankQuestionService questionBankQuestionService;

    @Resource
    private  AiManager aiManager;

    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Resource
    private EsAvailable esAvailable;

    @Resource
    @Lazy
    private QuestionThumbService questionThumbService;

    @Resource
    @Lazy
    private QuestionFavourService questionFavourService;

    @Resource
    @Lazy
    private UserQuestionService userQuestionService;

    @Resource
    private QuestionAssociateService questionAssociateService;

    @Resource
    private CounterManager counterManager;
    // 正则表达式设计
    final String TITLE_PATTERN = "(\\d+\\. .+?)\\n";
    final String SOLUTION_PATTERN = "( {3,}(?:\\n {3,}.*)*)";
    final String SECTION_PATTERN = "(?:\\*\\*[\\u4e00-\\u9fa5].*\\*\\*\\n(?: {4,}- .*\\n)+|```[\\s\\S]*?```)";

    Pattern pattern = Pattern.compile(
            TITLE_PATTERN + SOLUTION_PATTERN,
            Pattern.DOTALL | Pattern.MULTILINE
    );

    /**
     * 校验数据
     *
     * @param question
     * @param add      对创建的数据进行校验
     */
    @Override
    public void validQuestion(Question question, boolean add) {
        ThrowUtils.throwIf(question == null, ErrorCode.PARAMS_ERROR);
        // todo 从对象中取值
        String title = question.getTitle();
        String content = question.getContent();
        // 创建数据时，参数不能为空
        if (add) {
            // todo 补充校验规则
            ThrowUtils.throwIf(StringUtils.isBlank(title), ErrorCode.PARAMS_ERROR);
        }
        // 修改数据时，有参数则校验
        // todo 补充校验规则
        if (StringUtils.isNotBlank(title)) {
            ThrowUtils.throwIf(title.length() > 80, ErrorCode.PARAMS_ERROR, "标题过长");
        }
        if (StringUtils.isNotBlank(content)) {
            ThrowUtils.throwIf(content.length() > 8192, ErrorCode.PARAMS_ERROR, "内容过长");
        }
    }

    /**
     * 获取查询条件
     *
     * @param questionQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<Question> getQueryWrapper(QuestionQueryRequest questionQueryRequest) {
        QueryWrapper<Question> queryWrapper = new QueryWrapper<>();
        if (questionQueryRequest == null) {
            return queryWrapper;
        }
        // todo 从对象中取值
        Long id = questionQueryRequest.getId();
        Long notId = questionQueryRequest.getNotId();
        String title = questionQueryRequest.getTitle();
        String content = questionQueryRequest.getContent();
        String searchText = questionQueryRequest.getSearchText();
        String sortField = questionQueryRequest.getSortField();
        String sortOrder = questionQueryRequest.getSortOrder();
        List<String> tagList = questionQueryRequest.getTags();
        Long userId = questionQueryRequest.getUserId();
        String answer = questionQueryRequest.getAnswer();
        // todo 补充需要的查询条件
        // 从多字段中搜索
        if (StringUtils.isNotBlank(searchText)) {
            // 需要拼接查询条件
            queryWrapper.and(qw -> qw.like("title", searchText).or().like("content", searchText));
        }
        // 模糊查询
        queryWrapper.like(StringUtils.isNotBlank(title), "title", title);
        queryWrapper.like(StringUtils.isNotBlank(content), "content", content);
        queryWrapper.like(StringUtils.isNotBlank(answer), "answer", answer);
        // JSON 数组查询
        if (CollUtil.isNotEmpty(tagList)) {
            for (String tag : tagList) {
                queryWrapper.like("tags", "\"" + tag + "\"");
            }
        }
        // 精确查询
        queryWrapper.ne(ObjectUtils.isNotEmpty(notId), "id", notId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
        // 排序规则
        queryWrapper.orderBy(SqlUtils.validSortField(sortField),
                sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    /**
     * 获取题目表封装
     *
     * @param question
     * @param request
     * @return
     */
    @Override
    @HotKeyCheck(hotKey = "question_detail_")
    public QuestionVO getQuestionVO(Question question, HttpServletRequest request) {
        // 对象转封装类
        QuestionVO questionVO = QuestionVO.objToVo(question);

        // todo 可以根据需要为封装对象补充值，不需要的内容可以删除
        // region 可选
        // 1. 关联查询用户信息
        Long CreateUserId = question.getUserId();
        User user = null;
        if (CreateUserId != null && CreateUserId > 0) {
            user = userService.getById(CreateUserId);
        }
        User loginUser = userService.getLoginUser(request);
        Long questionId = question.getId();
        //监测爬虫
       crawlerDetect(loginUser.getId());

        UserVO userVO = userService.getUserVO(user);
        questionVO.setUser(userVO);

        //关联查询点赞收藏记录表

        questionVO.setHasThumb(questionThumbService.getStatus(questionId, loginUser.getId()));
         questionVO.setHasFavour(questionFavourService.getStatus(questionId, loginUser.getId()));

        //关联查询点赞数收藏数浏览数
        QueryWrapper<QuestionAssociate> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("questionId",questionId);
        QuestionAssociate questionAssociate = questionAssociateService.getOne(queryWrapper);
        if (questionAssociate!=null){
            questionVO.setThumbNum(questionAssociate.getThumNum());
            questionVO.setFavourNum(questionAssociate.getFavourNum());
            questionVO.setViewNum(questionAssociate.getViewNum());
            //每调用题目时，浏览数加1
            questionAssociate.setViewNum(questionAssociate.getViewNum()+1);
            questionAssociateService.updateById(questionAssociate);
        }else {
            questionAssociate = new QuestionAssociate();
            questionAssociate.setQuestionId(questionId);
            questionAssociate.setViewNum(1);
            questionAssociate.setThumNum(0);
            questionAssociate.setFavourNum(0);
            questionAssociateService.save(questionAssociate);
        }


        // endregion

        return questionVO;
    }

    /**
     * 分页获取题目表封装
     *
     * @param questionPage
     * @param request
     * @return
     */
    @Override
    public Page<QuestionVO> getQuestionVOPage(Page<Question> questionPage, HttpServletRequest request) {
        List<Question> questionList = questionPage.getRecords();
        Page<QuestionVO> questionVOPage = new Page<>(questionPage.getCurrent(), questionPage.getSize(), questionPage.getTotal());
        if (CollUtil.isEmpty(questionList)) {
            return questionVOPage;
        }
        // 对象列表 => 封装对象列表
        List<QuestionVO> questionVOList = questionList.stream().map(question -> {
            return QuestionVO.objToVo(question);
        }).collect(Collectors.toList());

        User user = userService.getLoginUserPermitNull(request);
        if (user!=null){
            Long userId = user.getId();
            //关联查询点赞收藏记录表和做题记录表
            List<Long> questionIdList = questionVOList.stream().map(QuestionVO::getId).collect(Collectors.toList());
            QueryWrapper<QuestionThumb> questionThumbQueryWrapper = new QueryWrapper<>();
            QueryWrapper<QuestionFavour> questionFavourQueryWrapper = new QueryWrapper<>();
            QueryWrapper<UserQuestion> userQuestionQueryWrapper = new QueryWrapper<>();
            if (CollUtil.isNotEmpty(questionIdList)) {
                questionThumbQueryWrapper.in("questionId", questionIdList);
                questionThumbQueryWrapper.eq("userId", userId);
                questionThumbQueryWrapper.eq("isCancel", 0);
                List<QuestionThumb> questionThumbList = questionThumbService.list(questionThumbQueryWrapper);

                questionFavourQueryWrapper.in("questionId", questionIdList);
                questionFavourQueryWrapper.eq("userId", userId);
                questionFavourQueryWrapper.eq("isCancel", 0);
                List<QuestionFavour> questionFavourList = questionFavourService.list(questionFavourQueryWrapper);

                userQuestionQueryWrapper.in("question_id", questionIdList);
                userQuestionQueryWrapper.eq("user_id", userId);
                List<UserQuestion> userQuestionList = userQuestionService.list(userQuestionQueryWrapper);

                //为每个题目设置点赞收藏状态
                questionVOList.forEach(questionVO -> {
                    questionVO.setHasThumb(questionThumbList.stream().anyMatch(questionThumb -> questionThumb.getQuestionId().equals(questionVO.getId())));
                    questionVO.setHasFavour(questionFavourList.stream().anyMatch(questionFavour -> questionFavour.getQuestionId().equals(questionVO.getId())));
                    questionVO.setHasWrite(userQuestionList.stream().anyMatch(userQuestion -> userQuestion.getQuestion_id().equals(questionVO.getId())));
                });
            }
        }


        questionVOPage.setRecords(questionVOList);
        return questionVOPage;
    }

    @Override
    public Page<Question> listPage(QuestionQueryRequest questionQueryRequest) {
        long current = questionQueryRequest.getCurrent();
        long size = questionQueryRequest.getPageSize();

        //题目表的查询条件
        QueryWrapper<Question> queryWrapper = this.getQueryWrapper(questionQueryRequest);
        //根据题库查询题目列表
        Long questionBankId = questionQueryRequest.getQuestionBankId();
        if (questionBankId != null){
            LambdaQueryWrapper<QuestionBankQuestion> eq = Wrappers.lambdaQuery(QuestionBankQuestion.class)
                    .select(QuestionBankQuestion::getQuestionId)
                    .eq(QuestionBankQuestion::getQuestionBankId, questionBankId);
            List<QuestionBankQuestion> questionList = questionBankQuestionService.list(eq);
            if (CollUtil.isNotEmpty(questionList)){
                Set<Long> questionIdList= questionList.stream()
                        .map(QuestionBankQuestion::getQuestionId)
                        .collect(Collectors.toSet());
                queryWrapper.in("id", questionIdList);
            }
        }
        // 查询数据库
        Page<Question> questionPage = this.page(new Page<>(current, size),queryWrapper);
        return questionPage;
    }

    @Override
    public boolean aiGenerateQuestion(String questionType, int number,User user) {
        if (ObjectUtil.hasEmpty(questionType,number,user)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"参数错误");
        }
        //定义系统prompt
        // 定义系统prompt
        String systemPrompt = "你是一个专业的编程面试题生成器，请根据以下要求生成结构化题目数据：\n" +
                "\n" +
                "【格式要求】\n" +
                "1. 每个题目必须包含以下4个字段，用换行符分隔：\n" +
                "   - title: 题目名称（简洁明确，不超过15字）\n" +
                "   - content: 题目描述（具体问题，不超过100字）\n" +
                "   - tags: 标签数组（JSON格式，如[\"JavaScript\",\"异步\"]）\n" +
                "\n" +
                "2. 题目间用\"---------\"分隔（共10个连字符）\n" +
                "\n" +
                "【题目要求】\n" +
                "要求输出格式如下：\n" +
                "要求是题目方向中经典的面试题：\n"+
                "\n" +
                "1. 什么是 Java 中的反射？\n" +
                "2. Java 8 中的 Stream API 有什么作用？\n" +
                "3. xxxxxx\n" +
                "\n" +
                "【重要提醒】\n" +
                "严格按照以上格式输出，不要添加任何额外的说明文字或标记。";
        //拼接用户prompt
        String userPrompt=String.format("题目数量：%s,题目方向：%s", number, questionType);
        //调用AI接口
        //处理数据插入数据库
        // 1. 预处理AI响应文本
        String rawAnswer = aiManager.doChat(systemPrompt, userPrompt);
        System.out.println("rawAnswer: " + rawAnswer);
// 2. 使用正则表达式精确匹配结构
        // 解析并处理数据
        List<Question> questionList = parseQuestionsSimple(rawAnswer);

        // 设置用户ID
        questionList.forEach(question -> question.setUserId(user.getId()));

        // 批量保存到数据库
        return this.saveBatch(questionList);

    }

    @Override
    public Page<Question> searchFromEs(QuestionQueryRequest questionQueryRequest) {

//        if (!esAvailable.isElasticsearchAvailable()){
//            // 如果 Elasticsearch 不可用，回退到数据库查询
//            log.warn("Elasticsearch is not available, falling back to database query");
//            //修改排序规则，因为es用的是得分排序
//            questionQueryRequest.setSortField("createTime");
//            return this.listPage(questionQueryRequest);
//        }
        // 获取参数
        Long id = questionQueryRequest.getId();
        Long notId = questionQueryRequest.getNotId();
        String searchText = questionQueryRequest.getSearchText();
        List<String> tags = questionQueryRequest.getTags();
        Long questionBankId = questionQueryRequest.getQuestionBankId();
        Long userId = questionQueryRequest.getUserId();
        // 注意，ES 的起始页为 0
        int current = questionQueryRequest.getCurrent() - 1;
        int pageSize = questionQueryRequest.getPageSize();
        String sortField = questionQueryRequest.getSortField();
        String sortOrder = questionQueryRequest.getSortOrder();

        // 构造查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 过滤
        boolQueryBuilder.filter(QueryBuilders.termQuery("isDelete", 0));
        if (id != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("id", id));
        }
        if (notId != null) {
            boolQueryBuilder.mustNot(QueryBuilders.termQuery("id", notId));
        }
        if (userId != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("userId", userId));
        }
        if (questionBankId != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("questionBankId", questionBankId));
        }
        // 必须包含所有标签
        if (CollUtil.isNotEmpty(tags)) {
            for (String tag : tags) {
                boolQueryBuilder.filter(QueryBuilders.termQuery("tags", tag));
            }
        }
        // 按关键词检索
        if (StringUtils.isNotBlank(searchText)) {
            boolQueryBuilder.should(QueryBuilders.matchQuery("title", searchText));
            boolQueryBuilder.should(QueryBuilders.matchQuery("content", searchText));
            boolQueryBuilder.should(QueryBuilders.matchQuery("answer", searchText));
            boolQueryBuilder.minimumShouldMatch(1);
        }
        // 排序
        SortBuilder<?> sortBuilder = SortBuilders.scoreSort();
        if (StringUtils.isNotBlank(sortField)) {
            sortBuilder = SortBuilders.fieldSort(sortField);
            sortBuilder.order(CommonConstant.SORT_ORDER_ASC.equals(sortOrder) ? SortOrder.ASC : SortOrder.DESC);
        }
        // 分页
        PageRequest pageRequest = PageRequest.of(current, pageSize);
        // 构造查询
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder)
                .withPageable(pageRequest)
                .withSorts(sortBuilder)
                .build();
        SearchHits<QuestionEsDTO> searchHits = elasticsearchRestTemplate.search(searchQuery, QuestionEsDTO.class);
        // 复用 Mybatis 的分页对象，封装返回结果
        Page<Question> page = new Page<>();
        page.setTotal(searchHits.getTotalHits());
        List<Question> resourceList = new ArrayList<>();
        if (searchHits.hasSearchHits()) {
            List<SearchHit<QuestionEsDTO>> searchHitList = searchHits.getSearchHits();
            for (SearchHit<QuestionEsDTO> questionEsDTOSearchHit : searchHitList) {
                resourceList.add(QuestionEsDTO.dtoToObj(questionEsDTOSearchHit.getContent()));
            }
        }
        page.setRecords(resourceList);
        return page;
    }

    @Override
    public Page<QuestionVO> getQuestionVOPage(Long current, Long size, List<Long> questionIdList, HttpServletRequest request) {
        // 查询数据库
        QueryWrapper<Question> questionQueryWrapper = new QueryWrapper<>();
        questionQueryWrapper.in("id", questionIdList);
        Page<Question> questionPage = this.page(new Page<>(current, size),questionQueryWrapper );
        Page<QuestionVO> questionVOPage = this.getQuestionVOPage(questionPage, request);
        return questionVOPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchDeleteQuestion(List<Long> questionIdList) {
        if (CollUtil.isEmpty(questionIdList)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "要删除的题目列表为空");
        }
        for (Long questionId : questionIdList) {
            boolean result = this.removeById(questionId);
            if (!result) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "删除题目失败");
            }
            // 移除题目题库关系
            LambdaQueryWrapper<QuestionBankQuestion> lambdaQueryWrapper = Wrappers.lambdaQuery(QuestionBankQuestion.class)
                    .eq(QuestionBankQuestion::getQuestionId, questionId);
            result = questionBankQuestionService.remove(lambdaQueryWrapper);
            if (!result) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "删除题目题库关联失败");
            }
        }
    }



    // 备用的简单分割方法
    public  List<Question> parseQuestionsSimple(String rawAnswer) {
        List<Question> questionList = new ArrayList<>();

        // 按分隔符分割问题
        String[] questionBlocks = rawAnswer.split("---------");

        for (String block : questionBlocks) {
            if (block.trim().isEmpty()) {
                continue;
            }

            Question question = parseSingleQuestion(block);
            if (question != null) {
                questionList.add(question);
            }
        }

        return questionList;
    }

    private  Question parseSingleQuestion(String block) {
        String[] lines = block.split("\n");
        StringBuilder title = new StringBuilder();
        StringBuilder content = new StringBuilder();
        StringBuilder tags = new StringBuilder();

        String currentField = null;

        for (String line : lines) {
            line = line.trim();
            if (line.isEmpty()) continue;

            if (line.startsWith("title:")) {
                currentField = "title";
                title.append(line.substring(6).trim());
            } else if (line.startsWith("content:")) {
                currentField = "content";
                content.append(line.substring(8).trim());
            } else if (line.startsWith("tags:")) {
                currentField = "tags";
                tags.append(line.substring(5).trim());
            }  else if (currentField != null) {
                // 继续当前字段的内容
                switch (currentField) {
                    case "title":
                        title.append(" ").append(line);
                        break;
                    case "content":
                        content.append(" ").append(line);
                        break;
                    case "tags":
                        tags.append(" ").append(line);
                        break;
                }
            }
        }
        String answer=aiGenerationAnswer(content.toString());

        // 确保所有必要字段都有值
        if (title.length() > 0 && content.length() > 0 && tags.length() > 0 && answer.length() > 0) {
            Question question=new Question();
            question.setTitle(title.toString());
            question.setAnswer(answer.toString());
            question.setContent(content.toString());
            question.setTags(tags.toString());
            return question;
        }

        return null;
    }
    private String aiGenerationAnswer(String question){
        String systemPrompt ="你是一位专业的程序员面试官，我会给你一道面试题，请帮我生成详细的题解。要求如下：\n" +
                "\n" +
                "1. 题解的语句要自然流畅\n" +
                "2. 题解可以先给出总结性的回答，再详细解释\n" +
                "3. 要使用 Markdown 语法输出\n" +
                "\n" +
                "除此之外，请不要输出任何多余的内容，不要输出开头、也不要输出结尾，只输出题解。\n" +
                "\n" +
                "接下来我会给你要生成的面试题\n";
        String userPrompt=String.format("面试题：%s", question);
        String answer=aiManager.doChat(systemPrompt, userPrompt);
        return answer;
    }
    /*
    *监测爬虫
     */
    private void crawlerDetect(long loginUserId){
        final int WARN_COUNT = 10;

        final int BAN_COUNT = 20;

        //拼接访问的key
        String key=String.format("user:access:%s",loginUserId);
        long count = counterManager.incrAndGetCounter(key, 1, TimeUnit.SECONDS, 180);
        if (count>BAN_COUNT){
            //踢下线
            StpUtil.kickout(loginUserId);

            //封号
            User user=new User();
            user.setId(loginUserId);
            user.setUserRole("ban");
            userService.updateById( user);
            StpUtil.disable(loginUserId, 3 * 24 * 60 * 60);
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR,"访问次数过多已封号三天");
        }
        if (count == WARN_COUNT){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"访问次数过多，请稍后再试");
        }
    }

}
